package ui;

import cmm.*;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.Token;

import javax.swing.*;
import javax.swing.event.UndoableEditEvent;
import javax.swing.event.UndoableEditListener;
import javax.swing.filechooser.FileFilter;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.text.*;
import javax.swing.undo.UndoManager;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.text.DateFormat;
import java.util.*;
import java.util.List;

/**
 * @author masker
 * @date 2017/12/2 下午4:27
 */
public class Editor extends JFrame implements ActionListener,
        UndoableEditListener {
    private RunThread runThread;
    private DebugThread debugThread;
    private static final String version = "CMM Editor v1.0";

    public static String readString = "";

    // 断点位置
    public Map<Element, Integer> traceBreakPoints = new HashMap<>(); // from 1 to x
    public boolean stop = false;
    public int nextBreakLine = 0; // from 1 to x
    private int currentLine = 0;

    private Style whiteStyle;
    private Style redStyle;

    public void changeHitLine(int newLine) {
        if (currentLine == newLine) {
            return;
        }
        StyledDocument document = editPane.getStyledDocument();
        if (currentLine != 0) {
            Element element = document.getDefaultRootElement().getElement(currentLine-1);
            document.setCharacterAttributes(element.getStartOffset(), element.getEndOffset()-element.getStartOffset(), whiteStyle, true);
        }
        if (newLine != 0) {
            Element element = document.getDefaultRootElement().getElement(newLine-1);
            document.setCharacterAttributes(element.getStartOffset(), element.getEndOffset()-element.getStartOffset(), redStyle, true);
        }
        currentLine = newLine;
    }

    private final UndoManager undoMgr = new UndoManager();
    // 上次保存后的文件名和地址
    private String fileName = "";
    private boolean saved = true;

    // 定义菜单项
    private JMenuItem newFile = new JMenuItem("新建");
    private JMenuItem open = new JMenuItem("打开");
    private JMenuItem save = new JMenuItem("保存 ");
    private JMenuItem lsave = new JMenuItem("另存为");
    private JMenuItem exit = new JMenuItem("退出");

    private JMenuItem cut = new JMenuItem("剪切 ");
    private JMenuItem copy = new JMenuItem("复制");
    private JMenuItem cast = new JMenuItem("粘贴");
    private JMenuItem delete = new JMenuItem("删除 ");
    private JMenuItem selectAll = new JMenuItem("全选");
//    private JMenuItem undo = new JMenuItem("Undo");
//    private JMenuItem redo = new JMenuItem("Redo");

    private JMenuItem lexer = new JMenuItem("词法分析");
    private JMenuItem parser = new JMenuItem("语法分析");
    private JMenuItem checkAll = new JMenuItem("全部");

    private JMenuItem run = new JMenuItem("运行");
    private JMenuItem debug = new JMenuItem("调试");
    private JMenuItem continu = new JMenuItem("继续");
    private JMenuItem next = new JMenuItem("下一步");
    private JMenuItem stopI = new JMenuItem("停止");

    private JCheckBoxMenuItem lexerTableCheck = new JCheckBoxMenuItem("单词表",
            true);
    private JCheckBoxMenuItem editPaneCheck = new JCheckBoxMenuItem("编辑面板",
            true);
    private JCheckBoxMenuItem consolePaneCheck = new JCheckBoxMenuItem("控制台",
            true);
    private JCheckBoxMenuItem parserPaneCheck = new JCheckBoxMenuItem("语法检查窗口",
            true);
    private JCheckBoxMenuItem variablePanelCheck = new JCheckBoxMenuItem("变量监测窗口", true);
    private JCheckBoxMenuItem midCodePanelCheck = new JCheckBoxMenuItem("中间代码窗口", true);

//	private JMenuItem inputAcc = new JMenuItem("输入");
//	private JMenuItem changeLog = new JMenuItem("Change Log");

    // 窗口
    private JScrollPane editSPane;
    private JPanel tokenPanel;
    private JPanel variablePanel;
    private JPanel tokenVariablePanel;
    private JPanel consolePanel;
    private JPanel parserPanel;
    private JPanel midCodePanel;
    private JPanel parserMidCodePanel;

    // 部件
    private Console console;

    // 尝试加入RowHeaderTable
    private JTable rowHeaderTable;

    private JTable midCodeTable;
    private JTable variableTable;
    private JTable tokenTable;
    private JTextArea parserArea;
//    private JDialog changeLogDialog;
    JTextPane editPane = new JTextPane() {
        public boolean getScrollableTracksViewportWidth() {
            return false;
        }

        public void setSize(Dimension d) {
            if (d.width < getParent().getSize().width) {
                d.width = getParent().getSize().width;
            }
            d.width += 100;
            super.setSize(d);
        }
    };

    // 构造函数
    public Editor() {
        super(version);
        this.setBounds(250, 50, 900, 650);
        this.setLayout(new BorderLayout());
        this.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
        this.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                int option = JOptionPane.showConfirmDialog(Editor.this,
                        "是否要退出？ ", "确认 ", JOptionPane.YES_NO_OPTION);

                if (option == JOptionPane.YES_OPTION)
                    if (e.getWindow() == Editor.this) {
                        System.exit(0);
                    }
            }
        });
        init();
    }

    private void init() {
        // 热键设置
        newFile.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, InputEvent.CTRL_MASK));
        open.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, InputEvent.CTRL_MASK));
        save.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_MASK));
        cut.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_X, InputEvent.CTRL_MASK));
        copy.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.CTRL_MASK));
        cast.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_V, InputEvent.CTRL_MASK));
        delete.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_D, InputEvent.CTRL_MASK));
        selectAll.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A, InputEvent.CTRL_MASK));
//        undo.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Z, InputEvent.CTRL_MASK));
//        redo.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Y, InputEvent.CTRL_MASK));
        run.setAccelerator(KeyStroke.getKeyStroke("F5"));
        debug.setAccelerator(KeyStroke.getKeyStroke("F6"));
        continu.setAccelerator(KeyStroke.getKeyStroke("F7"));
        next.setAccelerator(KeyStroke.getKeyStroke("F8"));
        stopI.setAccelerator(KeyStroke.getKeyStroke("F9"));

        whiteStyle = editPane.getStyledDocument().addStyle("white_style", null);
        redStyle = editPane.getStyledDocument().addStyle("red_style", null);
        StyleConstants.setBackground(whiteStyle, Color.WHITE);
        StyleConstants.setBackground(redStyle, new Color(0xFFD1E3));

        // 定义面板,滚动条和行号
        editSPane = new JScrollPane(editPane);

        // 定义面板的RowHeader，即行号
        rowHeaderTable = new JTable(new DefaultTableModel(0, 2)) {
            public boolean isCellEditable(int row, int column) {
                return false;
            }
        };
        rowHeaderTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
        rowHeaderTable.getColumnModel().getColumn(0).setPreferredWidth(10);
        rowHeaderTable.getColumnModel().getColumn(1).setPreferredWidth(18);
        rowHeaderTable.setRowHeight(17);
        rowHeaderTable.setPreferredScrollableViewportSize(new Dimension(28, 0));
        rowHeaderTable.setBackground(new Color(0xEAEAEA));
//        rowHeaderTable.setGridColor(new Color(0xE7E7E7));
        rowHeaderTable.setDefaultRenderer(Object.class, new DefaultTableCellRenderer() {
            public Component getTableCellRendererComponent(JTable table,
                                                           Object value, boolean isSelected, boolean hasFocus,
                                                           int row, int column) {
                super.getTableCellRendererComponent(table, value,
                        isSelected, hasFocus, row, column);
                if (column == 0) {
                    setHorizontalAlignment(RIGHT);
                    setForeground(Color.black);
                } else if (column == 1) {
                    setHorizontalAlignment(CENTER);
                    setForeground(Color.red);
                    if (traceBreakPoints.values().contains(row + 1)) {
                        setText("●");
                    } else {
                        setText("");
                    }
                }
                setBackground(new Color(0xEAEAEA));
                return this;
            }
        });
        rowHeaderTable.setGridColor(new Color(0xEAEAEA));
        rowHeaderTable.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent e) {
                int row = rowHeaderTable.getSelectedRow();
                int col = rowHeaderTable.getSelectedColumn();
                if(rowHeaderTable.isCellSelected(row, col)){
                    if (col == 1) {
                        if (rowHeaderTable.getValueAt(row, col).equals("●")) {
                            for (Element key : traceBreakPoints.keySet()) {
                                if (traceBreakPoints.get(key) == (row + 1)) {
                                    traceBreakPoints.remove(key);
                                    break;
                                }
                            }
                            rowHeaderTable.setValueAt("", row, col);
                        } else {
                            traceBreakPoints.put(editPane.getDocument().getDefaultRootElement().getElement(row), row + 1);
                            rowHeaderTable.setValueAt("●", row, col);
                        }
                    }
                }
            }
        });
        editSPane.setRowHeaderView(rowHeaderTable);

        this.add(editSPane, BorderLayout.CENTER);
        editPane.setFont(new Font("Courier New", Font.PLAIN, 14));
        editPane.getDocument().addDocumentListener(new SyntaxHighlighter(editPane, this));

        // 菜单栏的创建
        JMenuBar menuBar = new JMenuBar();
        this.setJMenuBar(menuBar);
        menuBar.setOpaque(true);
        JMenu file = new JMenu("文件");
        file.add(newFile);
        file.add(open);
        file.add(save);
        file.add(lsave);
        file.addSeparator();
        file.add(exit);
        menuBar.add(file);

        JMenu edit = new JMenu("编辑  ");
        edit.add(cut);
        edit.add(copy);
        edit.add(cast);
        edit.add(delete);
//        edit.addSeparator();
//        edit.add(undo);
//        edit.add(redo);
//        edit.addSeparator();
        edit.add(selectAll);
        menuBar.add(edit);

        JMenu check = new JMenu("检查");
        check.add(lexer);
        check.add(parser);
        check.addSeparator();
        check.add(checkAll);
//        check.addSeparator();
//        check.add(run);
//        check.add(debug);
        menuBar.add(check);

        JMenu window = new JMenu("窗口");
        window.add(lexerTableCheck);
        window.add(editPaneCheck);
        window.add(consolePaneCheck);
        window.add(parserPaneCheck);
        window.add(variablePanelCheck);
        window.add(midCodePanelCheck);
        menuBar.add(window);

        JMenu runMenu = new JMenu("运行");
        runMenu.add(run);
        runMenu.add(debug);
        runMenu.add(continu);
        runMenu.add(next);
        runMenu.add(stopI);
        menuBar.add(runMenu);

        // Token表
        tokenPanel = new JPanel();
        tokenPanel.setLayout(new BorderLayout());
        JLabel tokenLabel = new JLabel("单词表");
        tokenLabel.setHorizontalAlignment(0);
        tokenPanel.add(tokenLabel, BorderLayout.NORTH);
        this.tokenTable = new JTable(new DefaultTableModel(new String[][] {},
                new String[] { "行号", "单词", "类型" })) {
            public boolean isCellEditable(int row, int column) {
                return false;
            }
        };
        this.tokenTable.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
        this.tokenTable.setGridColor(Color.LIGHT_GRAY);
        JScrollPane tableSPane = new JScrollPane(this.tokenTable);
        tableSPane.setPreferredSize(new Dimension(200, 500));
        tokenPanel.add(tableSPane);
//        this.add(tokenPanel, BorderLayout.WEST);

        // 变量监视表
        variablePanel = new JPanel();
        variablePanel.setLayout(new BorderLayout());
        JLabel variableLabel = new JLabel("变量监视表");
        variableLabel.setHorizontalAlignment(0);
        variablePanel.add(variableLabel, BorderLayout.NORTH);
        this.variableTable = new JTable(new DefaultTableModel(new String[][] {},
                new String[] { "变量名", "值" })) {
            public boolean isCellEditable(int row, int column) {
                return false;
            }
        };
        this.variableTable.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
        this.variableTable.setGridColor(Color.LIGHT_GRAY);
        this.variableTable.getColumn(this.variableTable.getColumnName(0)).setPreferredWidth(50);
        this.variableTable.getColumn(this.variableTable.getColumnName(1)).setPreferredWidth(150);
        JScrollPane debugSpane = new JScrollPane(this.variableTable);
        debugSpane.setPreferredSize(new Dimension(200, 200));
        variablePanel.add(debugSpane);

        tokenVariablePanel = new JPanel();
        tokenVariablePanel.setLayout(new BorderLayout());
        tokenVariablePanel.add(variablePanel, BorderLayout.SOUTH);
        tokenVariablePanel.add(tokenPanel);
        this.add(tokenVariablePanel, BorderLayout.WEST);

        // console
        this.console = new Console();
        this.consolePanel = new JPanel();
        this.consolePanel.setLayout(new BorderLayout());
        this.consolePanel.setPreferredSize(new Dimension(800, 150));
        this.consolePanel.add(new JLabel("控制台窗口"), BorderLayout.NORTH);
        JScrollPane consolSPane = new JScrollPane(this.console);
        this.consolePanel.add(consolSPane, BorderLayout.CENTER);
        this.add(this.consolePanel, BorderLayout.SOUTH);

        // parser panel
        this.parserPanel = new JPanel();
        this.parserPanel.setLayout(new BorderLayout());
        this.parserPanel.add(new JLabel("语法检查结果"), BorderLayout.NORTH);
        this.parserPanel.setPreferredSize(new Dimension(220, 300));
        this.parserArea = new JTextArea();
        this.parserArea.setEditable(false);
        this.parserPanel.add(new JScrollPane(this.parserArea),
                BorderLayout.CENTER);
//        this.add(this.parserPanel, BorderLayout.EAST);

        // 中间代码表,四元式
        midCodePanel = new JPanel();
        midCodePanel.setLayout(new BorderLayout());
        JLabel midCodeLabel = new JLabel("中间代码");
        midCodeLabel.setHorizontalAlignment(0);
        midCodePanel.add(midCodeLabel, BorderLayout.NORTH);
        this.midCodeTable = new JTable(new DefaultTableModel(new String[][] {},
                new String[] { "line", "op", "arg1", "arg2", "result" })) {
            public boolean isCellEditable(int row, int column) {
                return false;
            }
        };
        this.midCodeTable.getColumnModel().getColumn(1).setPreferredWidth(100);
        this.midCodeTable.getColumnModel().getColumn(0).setPreferredWidth(40);
//        this.midCodeTable.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
//        this.midCodeTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
        this.midCodeTable.setGridColor(Color.LIGHT_GRAY);
        JScrollPane midCodeSpane = new JScrollPane(this.midCodeTable);
        midCodeSpane.setPreferredSize(new Dimension(300, 200));
        midCodePanel.add(midCodeSpane);

        parserMidCodePanel = new JPanel();
        parserMidCodePanel.setLayout(new BorderLayout());
        parserMidCodePanel.add(midCodePanel, BorderLayout.SOUTH);
        parserMidCodePanel.add(parserPanel);
        this.add(parserMidCodePanel, BorderLayout.EAST);

        // 增加监听器
        this.editPane.getDocument().addUndoableEditListener(this);
        newFile.addActionListener(this);
        open.addActionListener(this);
        save.addActionListener(this);
        lsave.addActionListener(this);
        exit.addActionListener(this);
        cut.addActionListener(this);
        copy.addActionListener(this);
        cast.addActionListener(this);
        delete.addActionListener(this);
        selectAll.addActionListener(this);
//        undo.addActionListener(this);
//        redo.addActionListener(this);
        lexer.addActionListener(this);
        parser.addActionListener(this);
        checkAll.addActionListener(this);
        run.addActionListener(this);
        debug.addActionListener(this);
        continu.addActionListener(this);
        next.addActionListener(this);
        stopI.addActionListener(this);
        lexerTableCheck.addActionListener(this);
        editPaneCheck.addActionListener(this);
        consolePaneCheck.addActionListener(this);
        parserPaneCheck.addActionListener(this);
        variablePanelCheck.addActionListener(this);
        midCodePanelCheck.addActionListener(this);

        // 初始化窗口设置
        this.tokenPanel.setVisible(lexerTableCheck.getState());
        this.variablePanel.setVisible(variablePanelCheck.getState());
        this.editSPane.setVisible(editPaneCheck.getState());
        //this.systemLAF.setSelected(true);
        this.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());


//        runThread = new RunThread(this);
//        debugThread = new DebugThread(this);
    }

    // 重写监听器方法
    public void actionPerformed(ActionEvent e) {

        String actionCommand = e.getActionCommand();

        if (e.getSource() == newFile) {
            newfile();
        } else if (e.getSource() == open) {
            openfile();
        } else if (e.getSource() == save) {
            savefile();
        } else if (e.getSource() == lsave) {
            lsavefile();
        } else if (e.getSource() == cut) {
            cutfile();
        } else if (e.getSource() == copy) {
            copyfile();
        } else if (e.getSource() == cast) {
            castfile();
        } else if (e.getSource() == delete) {
            deletefile();
        } else if (e.getSource() == selectAll) {
            this.editPane.setSelectionStart(0);
            this.editPane.setSelectionEnd(this.editPane.getText().length());
        }
//        else if (e.getSource() == undo) {
//            if (undoMgr.canUndo()) {
//                undoMgr.undo();
//            }
//        } else if (e.getSource() == redo) {
//            if (undoMgr.canRedo())
//                undoMgr.redo();
//        }
        else if (e.getSource() == lexer) {
            refreshLexerTable();
            this.tokenPanel.setVisible(true);
            this.lexerTableCheck.setState(true);
            variablePanel.setPreferredSize(new Dimension(200, 200));
        } else if (e.getSource() == parser) {
            refreshParserPanel();
            this.parserPanel.setVisible(true);
            this.parserPaneCheck.setState(true);
            midCodePanel.setPreferredSize(new Dimension(300, 200));
        } else if (e.getSource() == checkAll) {
            refreshLexerTable();
            this.tokenPanel.setVisible(true);
            this.lexerTableCheck.setState(true);
            variablePanel.setPreferredSize(new Dimension(200, 200));
            refreshParserPanel();
            this.parserPanel.setVisible(true);
            this.parserPaneCheck.setState(true);
            midCodePanel.setPreferredSize(new Dimension(300, 200));
        } else if (e.getSource() == run) {
            this.console.clear();
            variablePanelCheck.setState(false);
            this.variablePanel.setVisible(false);
            System.out.println("********** Run " + DateFormat.getDateTimeInstance().format(new Date()) + " **********");
            runThread = new RunThread(this);
            runThread.start();
        } else if (e.getSource() == debug) {
            this.console.clear();
            variablePanelCheck.setState(true);
            this.variablePanel.setVisible(true);
            this.stop = false;
            this.nextBreakLine = 0;
            System.out.println("********** Debug " + DateFormat.getDateTimeInstance().format(new Date()) + " **********");
            debugThread = new DebugThread(this);
            debugThread.start();
        } else if (e.getSource() == continu) {
            stop = false;
            nextBreakLine = 0;
        } else if (e.getSource() == next) {
            stop = false;
        } else if (e.getSource() == stopI) {
            if (debugThread != null) {
//                debugThread.interrupt();
                debugThread.stop();
                System.out.println("Process finished with exit code 1");
            }
        } else if (e.getSource() == lexerTableCheck) {
            this.tokenPanel.setVisible(lexerTableCheck.getState());
            if (!lexerTableCheck.getState()) {
                variablePanel.setPreferredSize(new Dimension(200, 456));
            } else {
                variablePanel.setPreferredSize(new Dimension(200, 200));
            }
        } else if (e.getSource() == editPaneCheck) {
            this.editSPane.setVisible(editPaneCheck.getState());
        } else if (e.getSource() == consolePaneCheck) {
            this.consolePanel.setVisible(consolePaneCheck.getState());
        } else if (e.getSource() == parserPaneCheck) {
            this.parserPanel.setVisible(parserPaneCheck.getState());
            if (!parserPaneCheck.getState()) {
                midCodePanel.setPreferredSize(new Dimension(300, 456));
            } else {
                midCodePanel.setPreferredSize(new Dimension(300, 200));
            }
        } else if (e.getSource() == variablePanelCheck) {
            this.variablePanel.setVisible(variablePanelCheck.getState());
        } else if (e.getSource() == midCodePanelCheck) {
            this.midCodePanel.setVisible(midCodePanelCheck.getState());
        }
        else if ("退出".equals(actionCommand)) {
            int option = JOptionPane.showConfirmDialog(Editor.this,
                    "是否要退出？ ", "确认 ", JOptionPane.YES_NO_OPTION);
            if (option == JOptionPane.YES_OPTION)
                System.exit(0);
        }

    }

    public void undoableEditHappened(UndoableEditEvent evt) {
        // System.out.println(evt.getEdit().getPresentationName());
        if (evt.getEdit().getPresentationName().equals("风格变化"))
            return;
        undoMgr.addEdit(evt.getEdit());
    }

    // 方法定义
    private void newfile() {

        JFileChooser fc = new JFileChooser(".");
        fc.setDialogTitle("新建文件");
        fc.setAcceptAllFileFilterUsed(false);
        fc.setFileFilter(new FileFilter() {
            public boolean accept(File f) {
                if (f.getName().endsWith(".cmm") || f.isDirectory()) {
                    return true;
                }
                return false;
            }

            public String getDescription() {
                return "CMM源程序(*.cmm)";
            }
        });

        if (fc.showSaveDialog(this) == JFileChooser.APPROVE_OPTION) {
            fileName = fc.getSelectedFile().getPath();
            if (!fileName.endsWith(".cmm"))
                fileName += ".cmm";
            File f = new File(fileName);
            editPane.setText(null);

            try {
                BufferedWriter bw = new BufferedWriter(new FileWriter(f));
                bw.write("", 0, 0);
            } catch (IOException e) {
                e.printStackTrace();
            }
            this.setTitle(version + " - " + fileName);
            this.saved = true;
        }

    }

    // 打开
    private void openfile() {
        JFileChooser fc = new JFileChooser(".");
        fc.setDialogTitle("打开");
        fc.setAcceptAllFileFilterUsed(false);
        fc.setFileFilter(new FileFilter() {
            public boolean accept(File f) {
                if (f.getName().endsWith(".cmm") || f.isDirectory()) {
                    return true;
                }
                return false;
            }

            public String getDescription() {
                return "CMM源程序(*.cmm)";
            }
        });
        if (fc.showOpenDialog(this) != JFileChooser.APPROVE_OPTION)
            return;
        // 建立新文件
        fileName = fc.getSelectedFile().getPath();
        if (!fileName.endsWith(".cmm"))
            fileName += ".cmm";
        File f = new File(fileName);
        // 用此文件的长度建立一个字符数组 （特别标注）
        char ch[] = new char[(int) f.length()];
        // 异常处理
        try {
            // 读出数据，并存入字符数组ch中
            BufferedReader br = new BufferedReader(new FileReader(f));
            br.read(ch);
            this.setTitle(version + " - " + fileName);
            this.saved = true;
            br.close();
        } catch (FileNotFoundException fe) {
            System.out.println("file not found");
            System.exit(0);
        } catch (IOException ie) {
            System.out.println("IO error");
            System.exit(0);
        }

        String s = new String(ch);
        editPane.setText(s);
    }

    // 保存
    private void savefile() {
        if (fileName.equals("")) {
            JFileChooser fc = new JFileChooser(".");
            fc.setDialogTitle("保存");
            fc.setAcceptAllFileFilterUsed(false);
            fc.setFileFilter(new FileFilter() {
                public boolean accept(File f) {
                    return (f.getName().endsWith(".cmm") || f.isDirectory());
                }

                @Override
                public String getDescription() {
                    return "CMM源程序(*.cmm)";
                }
            });
            String s = editPane.getText();

            if (fc.showSaveDialog(this) == JFileChooser.APPROVE_OPTION)
                try {
                    fileName = fc.getSelectedFile().getPath();
                    if (!fileName.endsWith(".cmm"))
                        fileName += ".cmm";
                    File f = new File(fileName);
                    BufferedWriter bw = new BufferedWriter(new FileWriter(f));
                    bw.write(s, 0, s.length());
                    this.setTitle(version + " - " + fileName);
                    this.saved = true;
                    bw.close();
                } catch (FileNotFoundException fe_) {
                    System.out.println("file not found");
                    System.exit(0);
                } catch (IOException ie_) {
                    System.out.println(" IO error");
                    System.exit(0);
                }
        }
        // 如果文件已经保存过
        else {
            String s = editPane.getText();

            try {
                File f = new File(fileName);

                BufferedWriter bw = new BufferedWriter(new FileWriter(f));
                bw.write(s, 0, s.length());
                this.setTitle(version + " - " + fileName);
                this.saved = true;
                bw.close();

            } catch (FileNotFoundException fe_) {
                System.out.println("file not found");
                System.exit(0);
            } catch (IOException ie_) {
                System.out.println(" IO error");
                System.exit(0);
            }

        }

    }

    // 另存为
    private void lsavefile() {
        JFileChooser fc = new JFileChooser(".");
        fc.setDialogTitle("另存为");
        fc.setAcceptAllFileFilterUsed(false);
        fc.setFileFilter(new FileFilter() {
            public boolean accept(File f) {
                if (f.getName().endsWith(".cmm") || f.isDirectory()) {
                    return true;
                }
                return false;
            }
            public String getDescription() {
                return "CMM源程序(*.cmm)";
            }
        });
        String s = editPane.getText();
        if (fc.showSaveDialog(this) == JFileChooser.APPROVE_OPTION)
            try {
                fileName = fc.getSelectedFile().getPath();
                if (!fileName.endsWith(".cmm"))
                    fileName += ".cmm";
                File f = new File(fileName);
                BufferedWriter bw = new BufferedWriter(new FileWriter(f));
                bw.write(s, 0, s.length());
                this.setTitle(version + " - " + fileName);
                this.saved = true;
                bw.close();
            } catch (FileNotFoundException fe_) {
                System.out.println("file not found");
                System.exit(0);
            } catch (IOException ie_) {
                System.out.println(" IO error");
                System.exit(0);
            }
    }

    // 剪切
    private void cutfile() {
        this.editPane.cut();
    }

    // 复制
    private void copyfile() {
        this.editPane.copy();
    }

    // 粘贴
    private void castfile() {
        this.editPane.paste();
    }

    // 删除
    private void deletefile() {
        int start = editPane.getSelectionStart();
        int end = editPane.getSelectionEnd();
        try {
            editPane.getDocument().remove(start, end-start);
        } catch (BadLocationException e) {
            undoMgr.discardAllEdits();
        }
    }

    // 生成单词表
    private void refreshLexerTable() {
        ((DefaultTableModel) this.tokenTable.getModel()).getDataVector()
                .removeAllElements();
        CMMLexer lex = new CMMLexer(CharStreams.fromString(this.editPane.getText()));
        CommonTokenStream tokens = new CommonTokenStream(lex);
        tokens.fill();
        for (Token token : tokens.getTokens()) {
            String s = token.getText();
            int lineNum = token.getLine();
            if (s.equals("<EOF>"))
                continue;
            String type = lex.typeTransfer(token.getType());
            ((DefaultTableModel) (this.tokenTable.getModel()))
                    .addRow(new String[] { "" + lineNum, s, type });
        }
    }

    public void refreshVariableTable() {
        DefaultTableModel model = (DefaultTableModel) this.variableTable.getModel();
        model.getDataVector().removeAllElements();
        for (Map<String, Integer> map : MidCodeDebug.intParasStack) {
            Set<String> keys = map.keySet();
            for (String key : keys) {
                model.addRow(new String[] { key, String.valueOf(map.get(key))});
            }
        }
        for (Map<String, Float> map : MidCodeDebug.doubleParasStack) {
            Set<String> keys = map.keySet();
            for (String key : keys) {
                model.addRow(new String[] { key, String.valueOf(map.get(key))});
            }
        }
        for (Map<String, ArrayList<Integer>> map : MidCodeDebug.intArrayParasStack) {
            Set<String> keys = map.keySet();
            for (String key : keys) {
                StringBuilder value = new StringBuilder("[ ");
                for (int i : map.get(key)) {
                    value.append(i);
                    value.append(", ");
                }
                value.delete(value.length()-2, value.length());
                value.append(" ]");
                model.addRow(new String[] { key, value.toString() });
            }
        }
        for (Map<String, ArrayList<Float>> map : MidCodeDebug.doubleArrayParasStack) {
            Set<String> keys = map.keySet();
            for (String key : keys) {
                StringBuilder value = new StringBuilder("[ ");
                for (float i : map.get(key)) {
                    value.append(i);
                    value.append(", ");
                }
                value.delete(value.length()-2, value.length());
                value.append(" ]");
                model.addRow(new String[] { key, value.toString() });
            }
        }
    }

    // 语法检查窗口更新
    public void refreshParserPanel() {
        CMMLexer lexer = new CMMLexer(CharStreams.fromString(this.editPane.getText()));
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        CMMParser parser = new CMMParser(tokens);
        parser.program();
        this.parserArea.setText(parser.getLogger());
    }

    private void changeRowHeaderTableWidth(int num, boolean enlarge) {
        int width = rowHeaderTable.getColumnModel().getColumn(0).getPreferredWidth();
        if (enlarge) {
            for (int i = 0; i < num; i++) {
                width += 9;
            }
        } else {
            for (int i = 0; i < num; i++) {
                width -= 9;
            }
        }
        rowHeaderTable.getColumnModel().getColumn(0).setPreferredWidth(width);
        rowHeaderTable.setPreferredScrollableViewportSize(new Dimension(width + 18, 0));
    }

    // 尝试加入RowHeaderTable
    // 外部监听器调用
    public void lineNumReset() {
        Element root = this.editPane.getDocument().getDefaultRootElement();
        int line = root.getElementCount();
        int size = rowHeaderTable.getModel().getRowCount();
        if (line == size) {
            return;
        } else if (line > size) {
            int newSpace = 0;
            int oldSpace = 0;
            int lineTemp = line, sizeTemp = size;
            while ((lineTemp /= 10) > 0) {
                newSpace++;
            }
            while ((sizeTemp /= 10) > 0) {
                oldSpace++;
            }
            if (oldSpace < newSpace) {
                int dis = newSpace - oldSpace;
                changeRowHeaderTableWidth(dis, true);
            }
            for (int i = size + 1; i <= line; i++) {
                ((DefaultTableModel)rowHeaderTable.getModel()).addRow(new String[]{"" + i, ""});
            }
        } else {
            int newSpace = 0;
            int oldSpace = 0;
            int lineTemp = line, sizeTemp = size;
            while ((lineTemp /= 10) > 0) {
                newSpace++;
            }
            while ((sizeTemp /= 10) > 0) {
                oldSpace++;
            }
            if (oldSpace > newSpace) {
                int dis = oldSpace - newSpace;
                changeRowHeaderTableWidth(dis, false);
            }
            for (int i = size; i > line; i--) {
                ((DefaultTableModel)rowHeaderTable.getModel()).removeRow(i - 1);
            }
        }

        Iterator<Element> iterator = traceBreakPoints.keySet().iterator();
        List<Element> deleteList = new LinkedList<>();
        while (iterator.hasNext()) {
            boolean found = false;
            Element e = iterator.next();
            for (int i = 0; i < line; i++) {
                if (e == root.getElement(i)) {
                    found = true;
                    traceBreakPoints.replace(e, i+1);
                    break;
                }
            }
            if (!found) {
//                traceBreakPoints.remove(e);
                deleteList.add(e);
            }
        }
        for (Element e : deleteList) {
            traceBreakPoints.remove(e);
        }
//        for (Element e : traceBreakPoints.keySet()) {
//            boolean found = false;
//            for (int i = 0; i < line; i++) {
//                if (e == root.getElement(i)) {
//                    found = true;
//                    traceBreakPoints.replace(e, i+1);
//                    break;
//                }
//            }
//            if (!found) {
//                traceBreakPoints.remove(e);
//            }
//        }
    }

    private void refreshBreakPoints() {
        DefaultTableModel model = (DefaultTableModel)rowHeaderTable.getModel();
        for (int i = 0; i < model.getRowCount(); i++) {
            model.setValueAt("", i, 1);
        }
        for (int i : traceBreakPoints.values()) {
            model.setValueAt("●", i, 1);
        }
    }

    public void refreshMidCode(Vector<MidCode> midCodes) {
        DefaultTableModel model = (DefaultTableModel)midCodeTable.getModel();
        model.getDataVector().removeAllElements();
        for (MidCode midCode : midCodes) {
            model.addRow(new String[] { "" + midCode.getNumber(), midCode.getOp().toString(), midCode.getArg1(), midCode.getArg2(), midCode.getResult() });
        }
    }

    public void titleReset() {
        if (!this.saved)
            return;
        if (this.fileName.equals("")) {
            this.setTitle(version + " - 新建文件*");
        } else {
            this.setTitle(version + " - " + fileName + "*");
        }
    }

    private void setLookAndFeel(String lnf) {

        try {
            UIManager.setLookAndFeel(lnf);
            SwingUtilities.updateComponentTreeUI(this);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void read(){
        this.console.inputAcc();
    }

    public static void main(String[] args)
            throws UnsupportedLookAndFeelException {
        Editor w = new Editor();
        w.setVisible(true);

    }

}